using System;
using SharpDX;

namespace Engine
{
	[Serializable]
	internal class Gjk
	{
		private static int[] BitsToIndices = new int[]
		{
			0, 
			1, 
			2, 
			17, 
			3, 
			25, 
			26, 
			209, 
			4, 
			33, 
			34, 
			273, 
			35, 
			281, 
			282, 
			2257
		};
		private Vector3 closestPoint;
		private Vector3[] y;
		private float[] yLengthSq;
		private Vector3[][] edges;
		private float[][] edgeLengthSq;
		private float[][] det;
		private int simplexBits;
		private float maxLengthSq;
		public bool FullSimplex
		{
			get
			{
				return this.simplexBits == 15;
			}
		}
		public float MaxLengthSquared
		{
			get
			{
				return this.maxLengthSq;
			}
		}
		public Vector3 ClosestPoint
		{
			get
			{
				return this.closestPoint;
			}
		}
		public Gjk()
		{
			this.y = new Vector3[4];
			this.yLengthSq = new float[4];
			this.edges = new Vector3[][]
			{
				new Vector3[4], 
				new Vector3[4], 
				new Vector3[4], 
				new Vector3[4]
			};
			this.edgeLengthSq = new float[][]
			{
				new float[4], 
				new float[4], 
				new float[4], 
				new float[4]
			};
			this.det = new float[16][];
			for (int i = 0; i < 16; i++)
			{
				this.det[i] = new float[4];
			}
		}
		public void Reset()
		{
			this.simplexBits = 0;
			this.maxLengthSq = 0f;
		}
		public bool AddSupportPoint(ref Vector3 newPoint)
		{
			int num = (Gjk.BitsToIndices[this.simplexBits ^ 15] & 7) - 1;
			this.y[num] = newPoint;
			this.yLengthSq[num] = newPoint.LengthSquared();
			for (int num2 = Gjk.BitsToIndices[this.simplexBits]; num2 != 0; num2 >>= 3)
			{
				int num3 = (num2 & 7) - 1;
				Vector3 vector = this.y[num3] - newPoint;
				this.edges[num3][num] = vector;
				this.edges[num][num3] = -vector;
				this.edgeLengthSq[num][num3] = (this.edgeLengthSq[num3][num] = vector.LengthSquared());
			}
			this.UpdateDeterminant(num);
			return this.UpdateSimplex(num);
		}
		private static float Dot(ref Vector3 a, ref Vector3 b)
		{
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}
		private void UpdateDeterminant(int xmIdx)
		{
			int num = 1 << xmIdx;
			this.det[num][xmIdx] = 1f;
			int num2 = Gjk.BitsToIndices[this.simplexBits];
			int num3 = num2;
			int num4 = 0;
			while (num3 != 0)
			{
				int num5 = (num3 & 7) - 1;
				int num6 = 1 << num5;
				int num7 = num6 | num;
				this.det[num7][num5] = Gjk.Dot(ref this.edges[xmIdx][num5], ref this.y[xmIdx]);
				this.det[num7][xmIdx] = Gjk.Dot(ref this.edges[num5][xmIdx], ref this.y[num5]);
				int num8 = num2;
				for (int i = 0; i < num4; i++)
				{
					int num9 = (num8 & 7) - 1;
					int num10 = 1 << num9;
					int num11 = num7 | num10;
					int num12 = (this.edgeLengthSq[num5][num9] < this.edgeLengthSq[xmIdx][num9]) ? num5 : xmIdx;
					this.det[num11][num9] = this.det[num7][num5] * Gjk.Dot(ref this.edges[num12][num9], ref this.y[num5]) + this.det[num7][xmIdx] * Gjk.Dot(ref this.edges[num12][num9], ref this.y[xmIdx]);
					num12 = ((this.edgeLengthSq[num9][num5] < this.edgeLengthSq[xmIdx][num5]) ? num9 : xmIdx);
					this.det[num11][num5] = this.det[num10 | num][num9] * Gjk.Dot(ref this.edges[num12][num5], ref this.y[num9]) + this.det[num10 | num][xmIdx] * Gjk.Dot(ref this.edges[num12][num5], ref this.y[xmIdx]);
					num12 = ((this.edgeLengthSq[num5][xmIdx] < this.edgeLengthSq[num9][xmIdx]) ? num5 : num9);
					this.det[num11][xmIdx] = this.det[num6 | num10][num9] * Gjk.Dot(ref this.edges[num12][xmIdx], ref this.y[num9]) + this.det[num6 | num10][num5] * Gjk.Dot(ref this.edges[num12][xmIdx], ref this.y[num5]);
					num8 >>= 3;
				}
				num3 >>= 3;
				num4++;
			}
			if ((this.simplexBits | num) == 15)
			{
				int num13 = (this.edgeLengthSq[1][0] < this.edgeLengthSq[2][0]) ? ((this.edgeLengthSq[1][0] < this.edgeLengthSq[3][0]) ? 1 : 3) : ((this.edgeLengthSq[2][0] < this.edgeLengthSq[3][0]) ? 2 : 3);
				this.det[15][0] = this.det[14][1] * Gjk.Dot(ref this.edges[num13][0], ref this.y[1]) + this.det[14][2] * Gjk.Dot(ref this.edges[num13][0], ref this.y[2]) + this.det[14][3] * Gjk.Dot(ref this.edges[num13][0], ref this.y[3]);
				num13 = ((this.edgeLengthSq[0][1] < this.edgeLengthSq[2][1]) ? ((this.edgeLengthSq[0][1] < this.edgeLengthSq[3][1]) ? 0 : 3) : ((this.edgeLengthSq[2][1] < this.edgeLengthSq[3][1]) ? 2 : 3));
				this.det[15][1] = this.det[13][0] * Gjk.Dot(ref this.edges[num13][1], ref this.y[0]) + this.det[13][2] * Gjk.Dot(ref this.edges[num13][1], ref this.y[2]) + this.det[13][3] * Gjk.Dot(ref this.edges[num13][1], ref this.y[3]);
				num13 = ((this.edgeLengthSq[0][2] < this.edgeLengthSq[1][2]) ? ((this.edgeLengthSq[0][2] < this.edgeLengthSq[3][2]) ? 0 : 3) : ((this.edgeLengthSq[1][2] < this.edgeLengthSq[3][2]) ? 1 : 3));
				this.det[15][2] = this.det[11][0] * Gjk.Dot(ref this.edges[num13][2], ref this.y[0]) + this.det[11][1] * Gjk.Dot(ref this.edges[num13][2], ref this.y[1]) + this.det[11][3] * Gjk.Dot(ref this.edges[num13][2], ref this.y[3]);
				num13 = ((this.edgeLengthSq[0][3] < this.edgeLengthSq[1][3]) ? ((this.edgeLengthSq[0][3] < this.edgeLengthSq[2][3]) ? 0 : 2) : ((this.edgeLengthSq[1][3] < this.edgeLengthSq[2][3]) ? 1 : 2));
				this.det[15][3] = this.det[7][0] * Gjk.Dot(ref this.edges[num13][3], ref this.y[0]) + this.det[7][1] * Gjk.Dot(ref this.edges[num13][3], ref this.y[1]) + this.det[7][2] * Gjk.Dot(ref this.edges[num13][3], ref this.y[2]);
			}
		}
		private bool UpdateSimplex(int newIndex)
		{
			int num = this.simplexBits | 1 << newIndex;
			int num2 = 1 << newIndex;
			for (int num3 = this.simplexBits; num3 != 0; num3--)
			{
				if ((num3 & num) == num3 && this.IsSatisfiesRule(num3 | num2, num))
				{
					this.simplexBits = (num3 | num2);
					this.closestPoint = this.ComputeClosestPoint();
					return true;
				}
			}
			bool result = false;
			if (this.IsSatisfiesRule(num2, num))
			{
				this.simplexBits = num2;
				this.closestPoint = this.y[newIndex];
				this.maxLengthSq = this.yLengthSq[newIndex];
				result = true;
			}
			return result;
		}
		private Vector3 ComputeClosestPoint()
		{
			float num = 0f;
			Vector3 vector = Vector3.Zero;
			this.maxLengthSq = 0f;
			for (int num2 = Gjk.BitsToIndices[this.simplexBits]; num2 != 0; num2 >>= 3)
			{
				int num3 = (num2 & 7) - 1;
				float num4 = this.det[this.simplexBits][num3];
				num += num4;
				vector += this.y[num3] * num4;
				this.maxLengthSq = Math.Max(this.maxLengthSq, this.yLengthSq[num3]);
			}
			return vector / num;
		}
		private bool IsSatisfiesRule(int xBits, int yBits)
		{
			bool result = true;
			for (int num = Gjk.BitsToIndices[yBits]; num != 0; num >>= 3)
			{
				int num2 = (num & 7) - 1;
				int num3 = 1 << num2;
				if ((num3 & xBits) != 0)
				{
					if (this.det[xBits][num2] <= 0f)
					{
						result = false;
						break;
					}
				}
				else
				{
					if (this.det[xBits | num3][num2] > 0f)
					{
						result = false;
						break;
					}
				}
			}
			return result;
		}
	}
}
